using BepuUtilities.Memory;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
#if MYCODE
using BepuUtilities.Vectors;
#else
#endif

namespace BepuPhysics.Trees
{
    public unsafe partial struct Tree
    {
        public Buffer<Node> Nodes;
        public Buffer<Metanode> Metanodes;
        int nodeCount;
        public int NodeCount
        {
            get
            {
                return nodeCount;
            }
        }

        public Buffer<Leaf> Leaves;
        int leafCount;
        public int LeafCount
        {
            get
            {
                return leafCount;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        int AllocateNode()
        {
            Debug.Assert(Nodes.Length > nodeCount && Metanodes.Length > nodeCount,
                "Any attempt to allocate a node should not overrun the allocated nodes. For all operations that allocate nodes, capacity should be preallocated.");
            return nodeCount++;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        int AddLeaf(int nodeIndex, int childIndex)
        {
            Debug.Assert(leafCount < Leaves.Length,
                "Any attempt to allocate a leaf should not overrun the allocated leaves. For all operations that allocate leaves, capacity should be preallocated.");
            Leaves[leafCount] = new Leaf(nodeIndex, childIndex);
            return leafCount++;
        }


        /// <summary>
        /// 构造一个空树。
        /// </summary>
        /// <param name="initialLeafCapacity">要分配空间的初始叶数。</param>
        public unsafe Tree(BufferPool pool, int initialLeafCapacity = 4096) : this()
        {
            if (initialLeafCapacity <= 0)
                throw new ArgumentException("Initial leaf capacity must be positive.");

            Resize(pool, initialLeafCapacity);

        }

        /// <summary>
        /// 从Serialize函数创建的字节缓冲区加载树。
        /// </summary>
        /// <param name="data">要加载到树中的数据。</param>
        /// <param name="pool">用于创建树的池。</param>
        public Tree(Span<byte> data, BufferPool pool)
        {
            if (data.Length <= 4)
                throw new ArgumentException($"Data is only {data.Length} bytes long; that's too small for even a header.");
            leafCount = Unsafe.As<byte, int>(ref data[0]);
            nodeCount = leafCount - 1;
            var leafByteCount = leafCount * sizeof(Leaf);
            var nodeByteCount = nodeCount * sizeof(Node);
            var metanodeByteCount = nodeCount * sizeof(Metanode);
            const int leavesStartIndex = 4;
            var nodesStartIndex = leavesStartIndex + leafByteCount;
            var metanodesStartIndex = nodesStartIndex + nodeByteCount;
            if (data.Length < leavesStartIndex + leafByteCount + nodeByteCount + metanodeByteCount)
                throw new ArgumentException($"Header suggested there were {leafCount} leaves, but there's not enough room in the data for that.");
            pool.Take(leafCount, out Leaves);
            pool.Take(nodeCount, out Nodes);
            pool.Take(nodeCount, out Metanodes);
            Unsafe.CopyBlockUnaligned(ref *(byte*)Leaves.Memory, ref data[leavesStartIndex], (uint)leafByteCount);
            Unsafe.CopyBlockUnaligned(ref *(byte*)Nodes.Memory, ref data[nodesStartIndex], (uint)nodeByteCount);
            Unsafe.CopyBlockUnaligned(ref *(byte*)Metanodes.Memory, ref data[metanodesStartIndex], (uint)metanodeByteCount);
        }

        /// <summary>
        /// 获取存储树所需的字节数。
        /// </summary>
        /// <param name="tree">要度量的树。</param>
        /// <return>存储树所需的字节数。</return>
        public int GetSerializedByteCount()
        {
            return 4 + sizeof(Leaf) * LeafCount + (sizeof(Node) + sizeof(Metanode)) * NodeCount;
        }

        /// <summary>
        /// 将树写入字节缓冲区。
        /// </summary>
        /// <param name="tree">要写入缓冲区的树。</param>
        /// <param name="bytes">保存树数据的缓冲区。</param>
        public void Serialize(Span<byte> bytes)
        {
            var requiredSizeInBytes = GetSerializedByteCount();
            if (bytes.Length < requiredSizeInBytes)
                throw new ArgumentException($"Target span size {bytes.Length} is less than the required size of {requiredSizeInBytes}.");
            Unsafe.As<byte, int>(ref bytes[0]) = LeafCount;
            var leafByteCount = LeafCount * sizeof(Leaf);
            var nodeByteCount = NodeCount * sizeof(Node);
            var metanodeByteCount = NodeCount * sizeof(Metanode);
            const int leavesStartIndex = 4;
            var nodesStartIndex = leavesStartIndex + leafByteCount;
            var metanodesStartIndex = nodesStartIndex + nodeByteCount;
            Unsafe.CopyBlockUnaligned(ref bytes[4], ref *(byte*)Leaves.Memory, (uint)leafByteCount);
            Unsafe.CopyBlockUnaligned(ref bytes[nodesStartIndex], ref *(byte*)Nodes.Memory, (uint)nodeByteCount);
            Unsafe.CopyBlockUnaligned(ref bytes[metanodesStartIndex], ref *(byte*)Metanodes.Memory, (uint)metanodeByteCount);
        }


        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Encode(int index)
        {
            return -1 - index;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        void InitializeRoot()
        {
            // 即使其中没有子项,根也始终存在。使一些记账变得更简单。
            nodeCount = 1;
            ref var rootMetanode = ref Metanodes[0];
            rootMetanode.Parent = -1;
            rootMetanode.IndexInParent = -1;
        }

        /// <summary>
        /// 调整支持树节点和树叶的缓冲区的大小。将不会将缓冲区缩小到当前驻留的节点和叶所需的大小以下。
        /// </summary>
        /// <param name="pool">从中获取和返回资源的池。</param>
        /// <param name="targetLeafSlotCount">可用叶槽的所需数量。</param>
        public void Resize(BufferPool pool, int targetLeafSlotCount)
        {
            // 请注意,调整大小低于可能使用的树叶的大小是不安全的。如果用户想要变小,他们需要先以某种方式显式地处理树叶。
            var leafCapacityForTarget = BufferPool.GetCapacityForCount<Leaf>(Math.Max(leafCount, targetLeafSlotCount));
            // 增量添加会检查叶子的容量,如果没有足够的空间,则会发出调整大小的命令。但是它不检查节点。
            // 您可以更改这一点,但目前,我们只需确保节点数组有足够的空间来容纳调整大小的叶数组中的所有内容。
            var nodeCapacityForTarget = BufferPool.GetCapacityForCount<Node>(Math.Max(nodeCount, leafCapacityForTarget - 1));
            var metanodeCapacityForTarget = BufferPool.GetCapacityForCount<Metanode>(Math.Max(nodeCount, leafCapacityForTarget - 1));
            bool wasAllocated = Leaves.Allocated;
            Debug.Assert(Leaves.Allocated == Nodes.Allocated);
            if (leafCapacityForTarget != Leaves.Length)
            {
                pool.ResizeToAtLeast(ref Leaves, leafCapacityForTarget, leafCount);
            }
            if (nodeCapacityForTarget != Nodes.Length)
            {
                pool.ResizeToAtLeast(ref Nodes, nodeCapacityForTarget, nodeCount);
            }
            if (metanodeCapacityForTarget != Metanodes.Length)
            {
                pool.ResizeToAtLeast(ref Metanodes, metanodeCapacityForTarget, nodeCount);
                // 节点的RefineFlag必须为0,因此只需清除节点集即可。
                // TODO：如果我们去掉了细化标志这个概念,那么这就没有必要了。
                Metanodes.Clear(nodeCount, Nodes.Length - nodeCount);
            }
            if (!wasAllocated)
            {
                InitializeRoot();
            }
        }


        /// <summary>
        /// 将树重置为新的构造后状态,清除树叶和节点,但保留后备资源不变。
        /// </summary>
        public void Clear()
        {
            leafCount = 0;
            InitializeRoot();
        }

        /// <summary>
        /// 释放树的后备资源,将它们返回到当前与树关联的池。
        /// </summary>
        /// <param name="pool">要将资源返回到的池。</param>
        /// <remarks>Disposed trees can be reused if EnsureCapacity or Resize is used to rehydrate them.</remarks>
        public void Dispose(BufferPool pool)
        {
            Debug.Assert(Nodes.Allocated == Leaves.Allocated && Nodes.Allocated == Metanodes.Allocated, "Nodes and leaves should have consistent lifetimes.");
            if (Nodes.Allocated)
            {
                pool.Return(ref Nodes);
                pool.Return(ref Metanodes);
                pool.Return(ref Leaves);
            }
        }

        /// <summary>
        /// 测试两个树引用是否指向相同的数据。
        /// </summary>
        /// <param name="a">要比较的第一个树。</param>
        /// <param name="b">要比较的第二个树。</param>
        /// <return>如果两个树具有相同的节点和节点计数,则为True,否则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool Equals(in Tree a, in Tree b)
        {
            return a.Nodes.Memory == b.Nodes.Memory && a.nodeCount == b.nodeCount;
        }

    }

}
